import models
import logging
import datetime
def lock(op):
	
	returnVal = False
	l=None
	if op.optype == "r" or op.optype == "w":
		l=models.Locks.getlockrecord(op.din)
		trans=op.transaction
		if l is None or l.mode is None:
			op.iswaiting=False
			l.mode = op.optype
			l.transactions.append(trans.key())
			l.din = op.din
			#this should perform logging also
			returnVal = True
		elif op.optype == "r" and l.mode == "r":
			op.iswaiting=False
			#both read locks, assign it
			l.transactions.append(trans.key())
			returnVal =  True
		elif len(l.transactions) == 1 and models.Transactions.get(l.transactions[0]).tid == trans.tid:
			#only this transaction itself holds 
			if op.optype == "w" and l.mode == "r":
				l.mode = "w"
			op.iswaiting=False
			returnVal = True
		else:
			#waitqueue
			op.iswaiting = True
			returnVal = False
		logging.info(op.iswaiting)
		if op.iswaiting and op.waitstarttime is None:
			op.waitstarttime = datetime.datetime.now()
		elif not op.iswaiting and op.waitstarttime is not None:
			op.waitendtime = datetime.datetime.now()
		models.save(op,l)
	elif op.optype == "c":
		logging.info("commit transaction for locking tid :%d is aborted %s" % (op.transaction.tid,str(op.transaction.isaborted)))
		trans = models.Transactions.gql("WHERE tid = :1",op.transaction.tid).get()
		#First of all find if this transaction has any operations in waiting queue if yes then add this to waiting queue
		#waitingop = Operation.gql("WHERE transaction = :1 and iswaiting = :2",trans,True).get();
		#waitingop = models.getwaitingops(trans)
		maxcount = 1
		#make maxcount 2 to factor in the fact that this transaction itself is waiting.
		if op.iswaiting:
			maxcount=2
		if not trans.haswaitingops(maxcount=maxcount):
			logging.info("doesnt have any waiting operations")
			#Doesnt have any waiting ops, ok to release locks and process wait queue
			op.perform()
			models.Log.add(op)
			processwaitqueue(trans)
			op.iswaiting = False
			returnVal = True
		else:
			logging.info("Cant perform commit, transaction has waiting operations")
			#has waiting ops, the commit must wait too
			op.iswaiting = True
			returnVal = False	
		if op.iswaiting and op.waitstarttime is None:
			op.waitstarttime = datetime.datetime.now()
		elif not op.iswaiting and op.waitstarttime is not None:
			op.waitendtime = datetime.datetime.now()

		models.save(op,l)
	if op.optype == "a":
		trans = models.Transactions.gql("WHERE tid = :1",op.transaction.tid).get()
		#no reason for abort to wait on a lock
		returnVal = True
		#remove waiting operations
		for waitingop in models.Operations.gql("WHERE transaction = :1 and iswaiting = :2",trans,True):
			trans.ops.remove(waitingop.key());
			#delete the waiting op
			waitingop.delete()
			#waitingop.iswaiting = False
			#models.save(waitingop)
			
		#abort transaction
		#find all write ops. perform inverse and then perform commit
		for op in models.Operations.gql("WHERE transaction = :1 and iswaiting = :2 and optype = :3",trans,False,"w"):
			inverseop = models.Operations(din=op.din,transaction=trans,beforeval=None,div=op.beforeval,optype="w",isinverseop=True)
			if lock(inverseop):
				inverseop.perform()
			else:
				logging.error("Cant get a write lock during inverse op, this shouldnt happen");
		commitop = models.Operations(din=None,transaction=trans,beforeval=None,div=None,optype="c")
		returnVal = lock(commitop)
	return returnVal
				
			

def processwaitqueue(trans):
	models.Locks.removetransaction(trans)
	logging.info("Processing wait queue")
	waitingops = models.getallwaitingops(trans)
	if not waitingops:
		logging.info("no waiting ops found")
	twait=set()
	for op in waitingops:
		logging.info("twait :%s" % str(twait))
		logging.info("Processing waitqueue op for %d type %s" % (op.transaction.tid,op.optype))
		if (op.optype == "r" or op.optype == "w") and (op.transaction.tid not in twait):
			if(lock(op)):
				logging.info("got lock now performing op")
				op.perform()
			else:
				logging.info("Couldnt get lock for waiting op trying next")
				twait.add(op.transaction.tid)
		elif (op.optype == "c") and (op.transaction.tid not in twait):
			if(lock(op)):
				return;
			else:
				twait.add(op.transaction.tid)
		

			
